﻿namespace Windows32
{
    using System;
    using System.Drawing;
    using System.Runtime.InteropServices;

    public class ReversibleDrawer : DeviceContextBase
    {
        private int intLineWidth;
        private int intOldReversible;
        private GDIPen myPen;

        private ReversibleDrawer()
        {
            this.intLineWidth = 1;
            this.intOldReversible = 0;
            this.myPen = new GDIPen(Color.Black);
        }

        public ReversibleDrawer(Graphics g)
        {
            this.intLineWidth = 1;
            this.intOldReversible = 0;
            this.myPen = new GDIPen(Color.Black);
            base.InitFromGraphics(g);
        }

        public void BeginReversible()
        {
            this.intOldReversible = SetROP2(base.intHDC, 6);
        }

        public override void Dispose()
        {
            base.Dispose();
            if (this.myPen != null)
            {
                this.myPen.Dispose();
                this.myPen = null;
            }
        }

        public void DrawLine(Point p1, Point p2)
        {
            this.DrawLine(p1.X, p1.Y, p2.X, p2.Y);
        }

        public void DrawLine(int x1, int y1, int x2, int y2)
        {
            int old = SetROP2(base.intHDC, 7);
            IntPtr oldb = base.SelectObject(StockObject.NULL_BRUSH);
            IntPtr oldp = base.SelectObject(this.myPen.Handle);
            this.myPen.DrawLine(base.intHDC, x1, y1, x2, y2);
            base.SelectObject(oldb);
            base.SelectObject(oldp);
            SetROP2(base.intHDC, old);
        }

        public void DrawLines(Point[] ps)
        {
            int old = SetROP2(base.intHDC, 6);
            this.myPen.DrawLines(base.intHDC, ps);
            SetROP2(base.intHDC, old);
        }

        public void DrawRectangle(System.Drawing.Rectangle rect)
        {
            this.DrawRectangle(rect.Left, rect.Top, rect.Width, rect.Height);
        }

        public void DrawRectangle(int left, int top, int width, int height)
        {
            int old = SetROP2(base.intHDC, 7);
            IntPtr oldb = base.SelectObject(StockObject.NULL_BRUSH);
            IntPtr oldp = base.SelectObject(this.myPen.Handle);
            Rectangle(base.intHDC, left, top, left + width, top + height);
            base.SelectObject(oldb);
            base.SelectObject(oldp);
            SetROP2(base.intHDC, old);
        }

        public void EndReversible()
        {
            SetROP2(base.intHDC, this.intOldReversible);
        }

        public void FillRectangle(System.Drawing.Rectangle rect)
        {
            this.FillRectangle(rect.Left, rect.Top, rect.Width, rect.Height);
        }

        public void FillRectangle(int left, int top, int width, int height)
        {
            RECT rc = new RECT();
            rc.left = left;
            rc.top = top;
            rc.right = left + width;
            rc.bottom = top + height;
            InvertRect(base.intHDC, ref rc);
        }

        public static ReversibleDrawer FromHDC(IntPtr hdc)
        {
            ReversibleDrawer rd = new ReversibleDrawer();
            rd.InitFromHDC(hdc);
            return rd;
        }

        public static ReversibleDrawer FromHwnd(IntPtr hwnd)
        {
            ReversibleDrawer rd = new ReversibleDrawer();
            rd.InitFromHWnd(hwnd);
            return rd;
        }

        public static ReversibleDrawer FromScreen()
        {
            ReversibleDrawer rd = new ReversibleDrawer();
            rd.InitFromHWnd(new IntPtr(0));
            return rd;
        }

        [DllImport("User32.dll", CharSet=CharSet.Auto)]
        private static extern int InvertRect(IntPtr hdc, ref RECT vRect);
        [DllImport("gdi32.dll", CharSet=CharSet.Auto)]
        private static extern bool LineTo(IntPtr hDC, int X, int Y);
        [DllImport("gdi32.dll", CharSet=CharSet.Auto)]
        private static extern bool MoveToEx(IntPtr hDC, int X, int Y, int lpPoint);
        [DllImport("gdi32.dll", CharSet=CharSet.Auto)]
        public static extern bool Rectangle(IntPtr hDC, int left, int top, int right, int bottom);
        [DllImport("gdi32.dll", CharSet=CharSet.Auto)]
        private static extern int SetROP2(IntPtr hDC, int DrawMode);

        public int LineWidth
        {
            get
            {
                return this.intLineWidth;
            }
            set
            {
                if ((this.intLineWidth != value) && (value > 0))
                {
                    this.intLineWidth = value;
                    this.myPen.Dispose();
                    this.myPen = new GDIPen(this.intLineWidth, Color.Black);
                }
            }
        }

        public Color PenColor
        {
            get
            {
                return this.myPen.Color;
            }
            set
            {
                this.myPen.Color = value;
            }
        }

        public Windows32.PenStyle PenStyle
        {
            get
            {
                return this.myPen.Style;
            }
            set
            {
                this.myPen.Style = value;
            }
        }

        private enum DCRasterOperations
        {
            R2_BLACK = 1,
            R2_COPYPEN = 13,
            R2_LAST = 0x10,
            R2_MASKNOTPEN = 3,
            R2_MASKPEN = 9,
            R2_MASKPENNOT = 5,
            R2_MERGENOTPEN = 12,
            R2_MERGEPEN = 15,
            R2_MERGEPENNOT = 14,
            R2_NOP = 11,
            R2_NOT = 6,
            R2_NOTCOPYPEN = 4,
            R2_NOTMASKPEN = 8,
            R2_NOTMERGEPEN = 2,
            R2_NOTXORPEN = 10,
            R2_WHITE = 0x10,
            R2_XORPEN = 7
        }

        [StructLayout(LayoutKind.Sequential)]
        private struct RECT
        {
            public int left;
            public int top;
            public int right;
            public int bottom;
        }
    }
}

